#include "lua.hpp"

#define fhndl FileHandle_t
#define MT_NAME "file"
#define MT_TYPE 1994
#define MT_GET Lua()->GetMetaTable(MT_NAME, MT_TYPE)

void g_getPath(const char* path) {
	size_t index = 0;
	std::string spath = std::string(path);
	while (true) {
		 index = spath.find("..", index);
		 if (index == std::string::npos) 
			 break;
		 spath.replace(index, 2, "");
		 ++index;
	}
	path = spath.c_str();
}

void g_fixSlashes(char* path) {
	while(*path) {
		if( *path == '\\' )
			*path = '/';
		path++;
	}
}

namespace file {
	LUA_FUNCTION( open ) {
		Lua()->CheckType(1, GLua::TYPE_STRING);
		Lua()->CheckType(2, GLua::TYPE_STRING);

		const char* path = Lua()->GetString(1);
		const char* type = Lua()->GetString(2);

		g_fixSlashes((char*)path);
		g_getPath(path);

		fhndl file = fileSystem->Open(path, type, "GAME");
		if(!file) {
			return 0;
		}
		Lua()->PushUserData(MT_GET, static_cast<void*>(file));
		return 1;
	}

	LUA_FUNCTION( exists ) {
		Lua()->CheckType(1, GLua::TYPE_STRING);
		const char* path = Lua()->GetString(1);
		Lua()->Push(fileSystem->FileExists(path, "GAME"));
		return 1;
	}

	LUA_FUNCTION( remove ) {
		Lua()->CheckType(1, GLua::TYPE_STRING);
		const char* path = Lua()->GetString(1);

		g_fixSlashes((char*)path);
		g_getPath(path);

		fileSystem->RemoveFile(path, "GAME");
		return 0;
	}

	LUA_FUNCTION( createdir ) {
		Lua()->CheckType(1, GLua::TYPE_STRING);
		const char* path = Lua()->GetString(1);

		g_fixSlashes((char*)path);
		g_getPath(path);

		fileSystem->CreateDirHierarchy(path, "GAME");
		return 0;
	}

	LUA_FUNCTION( read ) {
		Lua()->CheckType(1, MT_TYPE);
		fhndl file = reinterpret_cast<fhndl>(Lua()->GetUserData(1));
		if(!file) {
			return 0;
		}

		{
			unsigned char temp = 0;
			std::string data;
			while(!fileSystem->EndOfFile(file)) {
				if(fileSystem->Read(&temp, 1, file) == 0)
					break;
				if(temp == 0x00)
					break;
				data += temp;
			}
			Lua()->Push(data.c_str());
		}

		return 1;
	}

	LUA_FUNCTION( write ) {
		Lua()->CheckType(1, MT_TYPE);
		Lua()->CheckType(2, GLua::TYPE_STRING);
		fhndl file = reinterpret_cast<fhndl>(Lua()->GetUserData(1));
		if(!file) {
			return 0;
		}
		const char* data = Lua()->GetString(2);
		fileSystem->Write(data, V_strlen(data), file);
		return 0;
	}

	LUA_FUNCTION( size ) {
		Lua()->CheckType(1, MT_TYPE);
		fhndl file = reinterpret_cast<fhndl>(Lua()->GetUserData(1));
		if(!file) {
			return 0;
		}
		int size = fileSystem->Size(file);
		Lua()->Push((float)size);
		return 1;
	}

	LUA_FUNCTION( close ) {
		Lua()->CheckType(1, MT_TYPE);
		fhndl file = reinterpret_cast<fhndl>(Lua()->GetUserData(1));
		if(!file) {
			return 0;
		}
		fileSystem->Close(file);
		return 0;
	}
}

namespace io {
	void Lua::Initialize(lua_State *L) {
		CAutoUnRef io = Lua()->GetNewTable();
			io->SetMember("open", file::open);
			io->SetMember("exists", file::exists);
			io->SetMember("remove",  file::remove);
			io->SetMember("createdir", file::createdir);
		CAutoUnRef meta = MT_GET;
		{
				CAutoUnRef tbl = Lua()->GetNewTable();
					tbl->SetMember("read", file::read);
					tbl->SetMember("write", file::write);
					tbl->SetMember("size", file::size);
					tbl->SetMember("close", file::close);
				meta->SetMember("__index", tbl);
		}

		Lua()->SetGlobal("io", io);

		Lua()->SetGlobal("OPEN_READ", "r");
		Lua()->SetGlobal("OPEN_WRITE", "w");
		Lua()->SetGlobal("OPEN_APPEND", "a");
		Lua()->SetGlobal("OPEN_READWRITE", "r+");
		Lua()->SetGlobal("CREATE_READWRITE", "w+");
		Lua()->SetGlobal("CREATE_APPEND", "a+");
	}
}